package com.lizemin.mybatis.annotation;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.lizemin.mybatis.config.DataSourceProperties;
import com.lizemin.mybatis.constants.SqlConstants;
import com.lizemin.mybatis.entity.*;
import com.lizemin.mybatis.util.YamlConfigReader;
import com.mysql.cj.jdbc.MysqlDataSource;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.FileNotFoundException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.*;

/**
 * @author lzm
 * @date 2025/5/18 21:44
 * @description
 */
@Slf4j
public class ProxyBeanFactory {

    private static final HashMap<String, Map<String, MapperSql>> MAPPER_INFO = new HashMap<>();

    private static final MysqlDataSource dataSource;

    static {
        URL appUrl = ProxyBeanFactory.class.getClassLoader().getResource("application.yml");
        String applicationConfig = appUrl.getPath();
        DataSourceProperties dataSourceProperties;
        try {
            dataSourceProperties = YamlConfigReader.loadDataSourceProperties(applicationConfig);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
        dataSource = new MysqlDataSource();
        dataSource.setUser(dataSourceProperties.getUser());
        dataSource.setPassword(dataSourceProperties.getPassword());
        dataSource.setServerName(dataSourceProperties.getHostname());
        dataSource.setPort(Integer.parseInt(dataSourceProperties.getPort()));
        dataSource.setDatabaseName(dataSourceProperties.getDatabaseName());
        dataSource.setUrl("jdbc:mysql://" + dataSourceProperties.getHostname() + ":" + dataSourceProperties.getPort() + "/" + dataSourceProperties.getDatabaseName() + "?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false");

        log.info("初始化mybatis");
        String mapperLocation;
        try {
            Properties properties = YamlConfigReader.convertYamlToProperties(applicationConfig);
            mapperLocation = properties.getProperty("power.mybatis.mapper-package");
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }

        URL url = ProxyBeanFactory.class.getClassLoader().getResource(mapperLocation);
        if (Objects.isNull(url)) {
            throw new RuntimeException("mapper包不存在");
        }
        String path = url.getPath();
        File file = FileUtil.file(path);
        if (!file.isDirectory()) {
            throw new RuntimeException("mapper包必须是一个目录");
        }
        List<File> files = FileUtil.loopFiles(file);
        // 创建 SAXReader 实例
        SAXReader reader = new SAXReader();
        // 禁用 DTD 验证
        try {
            reader.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        } catch (SAXException e) {
            throw new RuntimeException(e);
        }
        for (File f : files) {
            try {
                // 解析 XML 文件
                Document document = reader.read(f);
                // 获取根节点
                Element root = document.getRootElement();
                Iterator<Element> it = root.elementIterator();
                HashMap<String, MapperSql> mapperContent = new HashMap<>();
                while (it.hasNext()) {
                    // 遍历根节点下的子节点
                    Element element = it.next();
                    HashMap<String, String> attributes = new HashMap<>();
                    for (Object attribute : element.attributes()) {
                        Attribute attr = (Attribute) attribute;
                        attributes.put(attr.getName(), attr.getValue());
                    }
                    String type = element.getName();
                    MapperSql mapperSql;
                    String id = attributes.get("id");
                    String sql = element.getText();
                    if (StrUtil.isBlank(sql)) {
                        throw new RuntimeException("sql语句不能为空");
                    }
                    sql = sql.replaceAll("(\n|\\s+)", " ").trim();
                    switch (type) {
                        case SqlConstants.SELECT:
                            SelectSql selectSql = new SelectSql();
                            selectSql.setType(type);
                            selectSql.setSql(sql);
                            selectSql.setId(id);
                            String resultType = attributes.get("resultType");
                            selectSql.setResultType(resultType);
                            mapperSql = selectSql;
                            break;
                        case SqlConstants.INSERT:
                            InsertSql insertSql = new InsertSql();
                            insertSql.setType(type);
                            insertSql.setSql(sql);
                            insertSql.setId(id);
                            String useGeneratedKeys = attributes.get("useGeneratedKeys");
                            insertSql.setUseGeneratedKeys(Boolean.parseBoolean(useGeneratedKeys));
                            String keyProperty = attributes.get("keyProperty");
                            insertSql.setKeyProperty(keyProperty);
                            mapperSql = insertSql;
                            break;
                        case SqlConstants.UPDATE:
                            UpdateSql updateSql = new UpdateSql();
                            updateSql.setType(type);
                            updateSql.setId(id);
                            updateSql.setSql(sql);
                            mapperSql = updateSql;
                            break;
                        case SqlConstants.DELETE:
                            DeleteSql deleteSql = new DeleteSql();
                            deleteSql.setType(type);
                            deleteSql.setId(id);
                            deleteSql.setSql(sql);
                            mapperSql = deleteSql;
                            break;
                        default:
                            throw new RuntimeException("不支持的sql类型");
                    }
                    mapperContent.put(id, mapperSql);
                }
                String namespace = root.attributeValue("namespace");
                MAPPER_INFO.put(namespace, mapperContent);
            } catch (DocumentException e) {
                throw new RuntimeException("解析xml文件失败");
            }
        }
    }


    /**
     * 获取代理对象
     *
     * @param clazz 接口类
     */
    @SuppressWarnings("unchecked")
    public static <T> T getProxy(Class<T> clazz) {
        if (!clazz.isInterface()) {
            throw new IllegalArgumentException("必须要是接口的class");
        }
        Map<String, MapperSql> sqlMap = MAPPER_INFO.get(clazz.getName());
        InvocationHandler handler = new MybatisInvocationHandler(sqlMap, dataSource);
        return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, handler);
    }


}
